package com.ysz.lock;

import com.google.common.collect.Lists;
import org.apache.curator.RetryLoop;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.WatcherRemoveCuratorFramework;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.utils.PathUtils;
import org.apache.curator.utils.ZKPaths;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @ClassName LockInternals
 * @Description 上锁解锁具体实现
 * @Data 13:58
 * @Version 1.0
 * @Author ysz
 */
public class LockInternals {

    private final WatcherRemoveCuratorFramework watcherRemoveCuratorFramework;
    private final String path;
    private final String basePath;
    private final LockInternalsDriver lockInternalsDriver;
    private final String lockName;
    private Thread currentThread;
    private final AtomicReference<RevocationSpec> atomicReference = new AtomicReference<RevocationSpec>(null);
    private final CuratorWatcher revocableWatcher = new CuratorWatcher() {
        @Override
        public void process(WatchedEvent event) throws Exception {

        }
    };

    private final Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            //在LockInternals对象上调用notifyAll()
            //源码是这么写的  但是这样我感觉有问题
            //watcherRemoveCuratorFramework.postSafeNotify(LockInternals.this);
            if (Objects.nonNull(currentThread)) {
                switch (currentThread.getState()) {
                    case WAITING:
                        watcherRemoveCuratorFramework.postSafeNotify(LockInternals.this);
                        break;
                    case RUNNABLE:
                    default:
                        currentThread.interrupt();
                }
            }
        }
    };

    private volatile int maxLeases;
    static final byte[] REVOKE_MESSAGE = "__REVOKE__".getBytes();


    public LockInternals(CuratorFramework client, LockInternalsDriver driver, String path, String lockName, int maxLeases) {
        this.watcherRemoveCuratorFramework = client.newWatcherRemoveCuratorFramework();
        this.lockName = lockName;
        this.maxLeases = maxLeases;
        this.lockInternalsDriver = driver;
        this.basePath = PathUtils.validatePath(path);
        //path: /myLock  lockName: /lock-  path: /myLock/lock-
        this.path = ZKPaths.makePath(path, lockName);
    }

    public String attemptLock() throws Exception {
        long startMillis = System.currentTimeMillis();
        int retryCount = 0;
        String ourPath = null;
        boolean hasTheLock = false;
        boolean isDone = false;
        while (!isDone) {
            isDone = true;
            try {
                //创建锁 返回锁路径  /锁根路径/锁路径
                ourPath = lockInternalsDriver.createsTheLock(this.watcherRemoveCuratorFramework, this.path, null);
                //根据锁路径判断是否需要阻塞(不是子节点列表中的头节点就需要阻塞 并监听头节点)
                hasTheLock = internalLockLoop(ourPath);
            } catch (Exception e) {
                //在创建锁和重试
                if (watcherRemoveCuratorFramework.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper())) {
                    isDone = false;
                } else {
                    throw e;
                }
            }
        }
        if (hasTheLock) {
            return ourPath;
        }
        return null;
    }

    public boolean internalLockLoop(String ourPath) throws Exception {
        boolean haveTheLock = false;
        boolean doDelete = false;
        try {
            //客户端状态是启动  且未拿到锁就循环
            while ((watcherRemoveCuratorFramework.getState() == CuratorFrameworkState.STARTED) && !haveTheLock) {
                //拿到锁的根路径下的所有子锁路径 并排好序
                List<String> sortedChildren = getSortedChildren();
                //拿到子锁路径
                String sequenceNodeName = ourPath.substring(basePath.length() + 1);
                //拿到要sequenceNodeName的上一个节点
                //拿不到 Result中的pathToWatch为null   getsTheLock为false
                Result result = lockInternalsDriver.getsTheLock(watcherRemoveCuratorFramework, sortedChildren, sequenceNodeName, maxLeases);
                //true表示sequenceNodeName为第一个节点，表示加锁成功直接返回
                if (result.isGetsTheLock()) {
                    haveTheLock = true;
                } else {
                    //拼接上一个节点(即要被监听的节点)
                    String previousPathToWatch = basePath + "/" + result.getPathToWatch();
                    synchronized (this) {
                        try {
                            //没有拿到锁监听上一个节点的变更，在监听中会调用notifyAll()唤醒下面的wait()
                            //此处用的监听api一定是要可以在监听时候，判断节点是否存在
                            //如果没有判断节点是否存在这个功能那么会死锁
                            //此处是通过异常外抛+重试的机制解决死锁问题(死锁：wait后没人唤醒他  notifyall没有被执行)
                            currentThread = Thread.currentThread();
                            watcherRemoveCuratorFramework.getData().usingWatcher(watcher).forPath(previousPathToWatch);
                            wait();
                        } catch (Exception e) {
                            e.printStackTrace();
                            //ignore
                            //要监听的节点不存在，不需要做任务处理，重复获取当前basePath下的节点然后进行监听即可
                            //因为在try中进行监听的时候，可能索要监听的节点已经被删除了
                            //即getSortedChildren()拿到的是脏数据，那么在执行监听时候会抛异常
                            //抛异常只需要重复获取锁根路径下的children列表即可
                            //重复执行加锁逻辑
                        }
                    }
                }
            }
        } catch (Exception e) {
            //监听中发生异常删除锁节点
            doDelete = true;
            throw e;
        } finally {
            if (doDelete) {
                deleteOurPath(ourPath);
            }
        }
        return haveTheLock;
    }

    public List<String> getSortedChildren() throws Exception {
        return this.getSortedChildren(watcherRemoveCuratorFramework, basePath, lockName, lockInternalsDriver);
    }

    public List<String> getSortedChildren(CuratorFramework client, String basePath, String lockName, LockInternalsDriver lockInternalsDriver) throws Exception {
        try {
            List<String> list = client.getChildren().forPath(basePath);
            ArrayList<String> arrayList = Lists.newArrayList(list);
            Collections.sort(arrayList, new Comparator<String>() {
                @Override
                public int compare(String completeWord1, String completeWord2) {
                    return lockInternalsDriver.fixString(completeWord1, lockName).compareTo(lockInternalsDriver.fixString(completeWord2, lockName));
                }
            });
            return arrayList;
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }

    public void deleteOurPath(String ourPath) {
        try {
            watcherRemoveCuratorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(ourPath);
        } catch (Exception e) {
            e.printStackTrace();
            //ignore
            //可能已经被删除了
        }
    }

    public boolean releaseLock(String lockPath) {
        try {
            watcherRemoveCuratorFramework.removeWatchers();
            deleteOurPath(lockPath);
        } catch (Exception e) {
            e.printStackTrace();
            deleteOurPath(lockPath);
        }
        return true;
    }
}
